<!DOCTYPE html>
<html lang="zh-cn">
  <head>
    <meta charset="UTF-8">
    <title>Sucha's Blog - Archive for June, 2020</title>
    <meta name="generator" content="MarkdownProjectCompositor.lua">
    <meta name="author" content="Sucha">
    <meta name="keywords" content="suchang, programming, Linux, Lua">
    <meta name="description" content="Sucha's blog">
    <link rel="shortcut icon" href="../images/ico.png">
    <link rel="stylesheet" type="text/css" href="../styles/blog.css">
    <link rel="stylesheet" type="text/css" href="../styles/prism.min.css">
    <style id="site_theme"></style>
  </head>
  <body>
    <div id="body">
      <div id="text">
	   <!-- Page published by cmark-gfm begins here --><h1>Sucha's Blog ~ Archive for June, 2020</h1>
<p><a id="p2"></a></p>
<div class="date">20年6月21日 周日 11:39</div>
<h2>Text Rank 算法</h2>
<p><a href="https://web.eecs.umich.edu/~mihalcea/papers/mihalcea.emnlp04.pdf">TextRank</a> 是一种抽取式自动文摘算法，随便在网上搜一下相关，或者在 github 上面搜一下，介绍都一大把的。可是绝大部分的文章都只是堆 python 然后说要怎么做，也许是我的理解能力问题，在有限的几步里面，不晓得这么做的原因，会感觉很头疼。</p>
<p>这里尝试从大的方面还原这个算法，然后给出自己的实现，不过感觉自己的实现有些问题，效果没有想象中那么好，不过也总比没有强吧。</p>
<p>先开始吧。</p>
<p>TextRank 是一种抽取式的文章摘要算法，其最后的输出内容，都来源于文档本身的句子。这些句子会被算法打分排序，依靠的其实是 <a href="https://google.fandom.com/wiki/PageRank">PageRank</a> 算法。</p>
<h3>PageRank</h3>
<p>PageRank 算法原来只是为了描述网页间的重要程度，重要程度的计算，来源于他们间的超链接关系。简单的说，被链接得越多的页面，越重要，跟重要页面有链接的页面，比其他的页面更重要。准确分值的计算，是根据下面的公式来的：</p>
<p><img src="https://pic1.zhimg.com/80/v2-0d6c3c27eeee373e89b02e57630df6e4_720w.jpg" alt="img"></p>
<p>其中，PR(Vi) 表示网页 Vi 的分值，In(Vi) 表示 PR(Vi) 包含的网页集合，Out(Vj) 表示 PR(Vi) 链接出去的网页集合，d 是链接传递系数或者说网页跳转的可能性，常用的值是 0.85。</p>
<p>可以把这个公式的计算基础描述成一张图，网页就是节点 PR(Vi)，边就是跟 PR(Vi) 有关系的 PR(Vj) 链接集合。</p>
<p>初始化的时候，各个 PR(Vi) 的分值是不确定的，只有边的链接描述，因此需要迭代计算，比如都给各个 PR(Vi) 初始化为 1，通过公式多轮不断的迭代计算，当各个 PR(Vi) 分值稳定的时候，就是各个网页的 rank 分值。</p>
<h3>Sentence simularity</h3>
<p>如果把上面的 PR(Vi) 当成句子，我们就可以套用 PageRank 公式计算出句子的 rank 值，并提取重要程度高的句子作为文章摘要描述。</p>
<p>但是如何确定不同的句子有边的联系呢，算法提供了计算句子间相似程度这个标准，使用下面的公式：</p>
<p><img src="https://pic4.zhimg.com/80/v2-6f2cdbb2e228a23d26ba303c9e958017_720w.jpg" alt="img"></p>
<p>其中 wk 是句子 Si 及 Sj 都有的公共词组，分母是词组的数量。当然，也可以用句子的余弦相似度来计算，但是词组本身就得包含向量值了，这些词组，可以称为 word vector。</p>
<h3>Word Vector</h3>
<p>词组在中文里涉及到分词，有不同的工具可以用来分词表来构建语料的词向量表，比如有的用 <a href="https://pathmind.com/wiki/word2vec">word2vec</a>，有的用 <a href="https://nlp.stanford.edu/projects/glove/">GloVe</a>。话说，除了上面的模型、算法之外，语料可是相当重要的基础资源呀。</p>
<p>这里当然还涉及到使用哪些范畴的语料来构建这个 word vector，这里不详细叙述了，因为网上相关资源就更多了。</p>
<h3>最后</h3>
<p>TextRank 算法应该就是上面这些了吧，虽然为了搞清楚查了很久，最后推一把自己堆的 <a href="https://github.com/lalawue/text_rank">text_rank</a>，基于 LuaJIT 和 <a href="https://github.com/thunlp/THULAC">THULAC</a>，随便跑起来，内存就占用了 1 个 G，搞不起搞不起。</p>
<div class="category"><a href="CategoryStatistics.html">CategoryStatistics</a> / <a href="2020-06.html#p2">Permalink</a> / <a href="https://github.com/lalawue/homepage/discussions/categories/blog" target="_blank">Discussion</a></div>
<p><a id="p1"></a></p>
<div class="date">20年6月6日 周六 19:29</div>
<h2>Lua 的优点</h2>
<p>接着上一篇 <a href="../blog/2020-06.html#p0">Lua 的缺点</a>，作为平衡，这里补充一下。</p>
<p>作为一个普普通通，用过 ObjC、C/C++、Java、Swift、Emacs Lisp 的开发者，也是可以把 Lua 的一些好用的方面吹一下的。由于没用过 Python、Ruby、JavaScript，下面的观点未必就是 Lua 对比其他脚本语言鲜明的优点。</p>
<p>其实呢，我是将 Lua 跟上面所列的编译型、静态类型的语言做对比的，:)</p>
<h3>Lua 是简单的</h3>
<p>Lua 是命令式的语言，感觉以 C/C++、Java 起步的同学，学习 Lua 完全没有难度，Lua 语言没有几个关键字，控制结构甚至没有 continue。</p>
<p>而容器结构，只有一个 table，融合了 Array 以及 HashMap，当然，table 的作用不止于此，后面有更进一步的描述。</p>
<h3>Lua 很小易于移植</h3>
<p>Lua 语言只有核心部分，VM 代码只有几万行，是古老的 C89 语法，而且运行期的 VM 内存占用也不多，大概几百 kb 这样。</p>
<p>这使得 Lua 的编译安装是毫无难度的，支持 C89 的 C 编译器、硬件遍地都是。较少的资源消耗，意味着，在如今的操作系统上，可以大大方方的将几十、几百个 Lua VM 跑起来。</p>
<p>Lua 的核心还可以配置，将 Number 配置为 32bit 的 Integer，在低端没有浮点数的平台上，也能使用。好早之前，我就在 Rockchip 的的平台上应用上了 Lua，没有浮点数，Number 设置为了 32bit 的 Integer，用得好好的。</p>
<p>所以，不用担心写的纯 Lua 代码是不是在某些平台跑不起来，不存在的。</p>
<h3>Lua 很稳定很快</h3>
<p>Lua 生于 1993 年 7 月 28 日，语言核心早就稳定下来了，工业界早已采用多年。</p>
<p>LuaJIT 很快很快，可以看一下 LuaJIT-2.0 对比 Lua-5.1.5 版本的效率对比 <a href="http://luajit.org/performance_x86.html">Performance: x86/x64</a>。如今 Redis、OpenResty 标配 LuaJIT 作为内建的脚本支持。</p>
<h3>Lua 是动态类型的</h3>
<p>写应用程序的同学都了解，声明一个变量再去声明类型是有多重复，所以最新的 C++、Swift 对于类型都是可以自动推断的了。</p>
<p>我是觉得对于模块化设计来说，类型只在模块输入、输出的时候才有作用，比如对于输入的数据，我要知道变量的大小和范围，这也是模块对于这个输入参数声明的支持范围。</p>
<p>但如果真的需要程序关心内存布局的时候，每一个变量需要多大的内存空间，才值得去斟酌，而类型，其实就是内存空间占用的映射。模块内部，既然都是可信的环境，用足够大的内存区域去持有这个变量就行了，这个不影响，反而少了类似 C 这样因为类型转换而截断，导致期望的判断没有成功，引起模块内部错误，不得不抛出异常或者出错，其实毫无必要。</p>
<p>从另外一方面说，如果有了静态类型，可以控制任何一段程序在变量传递、比较时候的范围，但是当我们需要更高程度的抽象的时候，就又涉及到了如何抽象类型，这又涉及到了模版、范型。感觉又复杂了，也许这是所有静态类型的语言，都绕不过去的一个弯吧。</p>
<h3>Lua 支持多种编程模式</h3>
<p>Lua 中可以加载一段 String、一个文件并运行，简单的语法，使得数据描述编程、数据驱动编程是完全可行的。比如我在 Lua 脚本中加载自己，在运行前修改其流程，再运行，是完全可行的。</p>
<p>Lua 的函数是一等公民，可以实现 <a href="https://blog.csdn.net/yuanlin2008/article/details/8627081">lambada 算子</a>，函数式编程妥妥的没问题。</p>
<p>Lua 可以实现面向对象编程，上面有提到 table，当在 Lua 里面实现面向对象的时候，对象实例其实就是一个 table，对象的属性只不过是 table 里面的一个 key，函数跟 Number、String 一样可以作为 key 的 value。</p>
<p>当这样调用对象方法时，table:func(value)，func 默认第一个参数会传递 self，就是对象自己，一个 table，第二个参数才是 value。</p>
<p>理解 Lua 的面向对象编程，需要了解 Lua 内建的细节了，比如 table 的 __index key 是指找值的时候应该去哪里找，__newindex 则是创建新值的时候应该怎么做。多说一句，如果想 table 是 readonly 的，修改 table 的 __newindex 就对了。</p>
<p>下面是 Lua 的面向对象编程类定义的一个例子，a = Object.newObject(1), b = Object.newObject(2), a:isEqual(b) 将返回 false。</p>
<pre><code class="language-lua">local Object = {
    value = 0
}
Object.__index = Object

function Object.newObject(value)
    local obj = setmetatable({}, Object)
    obj.value = value
    return obj
end

function Object:isEqual(obj)
    return self.value == obj.value
end

function Object:isGreater(obj)
    return self.value &gt; obj.value
end

return Object
</code></pre>
<p>更进一步，Lua 的面向对象可以定制 __index 来实现单继承，甚至多继承，实例变量、类变量等，面向对象用这个库就对了 <a href="https://github.com/kikito/middleclass/wiki">kikito/middleclass</a>。</p>
<h3>Lua 元表</h3>
<p>话说 table 在 Lua 中占据了中心位置，既是 Array 又是 HashMap，面向对象的时候又是对象实例本身，定义的时候用到了，实例运行期间也用到了。</p>
<p>table 有这么多的身份，定义不同身份时候的行为，成了关键。在 Lua 里面，table 的 key 怎么寻找，怎么设置新值，两个 table 之间怎么相加、相减等等，是通过定义 table 的 metatable （元表）来实现的。</p>
<p>面向对象的例子，就用了 Lua 的内建函数 setmetatable，定义了 Object.__index 的行为，限制 key 值应该在哪里寻找。</p>
<p>这里有一篇 <a href="https://www.runoob.com/lua/lua-metatables.html">Lua 元表</a> 的教程。</p>
<p>多说两句吧，结合面向对象 Object 的例子，我们定义一下两个 Object 相加的行为：</p>
<pre><code class="language-lua">local Object = {}
Object.__index = Object
Object.__add = function(oobj, nobj)
    oobj.value = oobj.value + nobj.value
    return oobj
end
</code></pre>
<p>这样，如果 a = Object.newObject(1), b = Object.newObject(2), a = a + b 后，a.value 等于 3。</p>
<p>话说连 java 都没能实现的运算符重载，Lua 通过 table 的 metatable 实现了。</p>
<h3>Lua 跟 C 的交互</h3>
<p>官方 Lua 跟 C 的交互，需要用到 Lua 的 C API 定义 Lua 可以使用的 C 库，在这一层 wrapper 中再调用其他编译、链接进来的其他库，可以看下这篇 <a href="https://blog.csdn.net/AlexWoo0501/article/details/50916037">Lua C API 研究 —— 基础篇</a>，英文的 <a href="https://chsasank.github.io/lua-c-wrapping.html">Exposing C functions to Lua</a>。</p>
<p>如果用的是 LuaJIT，对于已有 C 库的使用，就方便多了，用到了内建的 ffi 库来声明二进制库中声明的函数接口、用到的结构等，官方教程及例子 <a href="http://luajit.org/ext_ffi_tutorial.html">FFI Tutorial</a>，这里不单独列举了。</p>
<p>--</p>
<p>Lua 作为一种简单、稳定、快速的命令式语言，提供高度的抽象，又可以很方便地使用已有的 C 库资源，对于我这种从 C/C++ 起步的程序员来说，相当友好。</p>
<p>酒醒了，吹一波不为过，:)</p>
<div class="category"><a href="CategoryProgramming.html">CategoryProgramming</a> / <a href="2020-06.html#p1">Permalink</a> / <a href="https://github.com/lalawue/homepage/discussions/categories/blog" target="_blank">Discussion</a></div>
<p><a id="p0"></a></p>
<div class="date">20年6月5日 周五 23:56</div>
<h2>Lua 的缺点</h2>
<p>在官网的 <a href="https://www.lua.org/about.html">关于 Lua</a> 页面，是这么介绍 Lua 的：</p>
<pre><code class="language-source">Lua 是一种强大、高效、轻量，可嵌入的脚本语言。支持过程编程，面向对象编程，函数式编程，数据驱动编程，以及数据描述编程。
</code></pre>
<p>虽然 Lua 官网将自己介绍为脚本语言，但业界对于 Lua 的定位及使用早已超出普通脚本的范围。比如，<a href="https://wowwiki.fandom.com/wiki/Lua">魔兽争霸 3</a>，以及 Adobe 的 <a href="https://www.adobe.io/apis/creativecloud/lightroomclassic.html">Lightmoom</a> 都大量使用了 Lua 来构建桌面程序；在后端组件中大名鼎鼎的 Redis 以及 OpenResty，则将 Lua 作为了内嵌的业务处理语言；而在 github 上 star 数量为 9k 的 <a href="https://github.com/cloudwu/skynet">skynet</a>，则更进一步地将 Lua 定位为了后端的业务逻辑语言。</p>
<p>虽然 Lua 运行很快，业界的效率标杆 LuaJIT 是所有其他脚本语言用于比对的塔顶，要不然 Redis、OpenResty 怎会将其作为内嵌的扩展方案呢。</p>
<p>但是 Lua 并没有流行开来，对于一个创建于 1993 年 7 月 28 日的语言来说，缺的应该不是机遇，而是其他的一些东西。</p>
<p>我也用了不少时间的 Lua，作为一名普通的应用程序开发者，就吐槽一下吧。</p>
<h3>Lua 是分裂的</h3>
<p>Lua 是分裂的，不说别的，单就官方版本的 Lua 5.3.5 以及 LuaJIT，其实是两种不同的语言，LuaJIT 定在了 Lua 5.1.5 版本，且大量应用在了 Redis 以及 OpenResty 中。</p>
<p>而且因为 Lua 的核心很小，大量的功能都用 C 或者 C++ 构建，通过 C API 提供给 Lua 虚拟机调用，但是不同
的 Lua 版本，其实 C API 差异很大，几乎就是不同的语言。</p>
<p>很多将 Lua 作为嵌入脚本语言的的桌面程序，将其使用的 Lua 版本固定下来了，其 C API 构建的 Lua 库也都固定了，其他版本的 Lua 可不一定能使用上这个库，而这个提供给 Lua 使用的 C 库，也不是向前向后兼容的。</p>
<h3>Lua 是动态类型的</h3>
<p>这是一个双刃剑，这里只说不好的方面吧。如果一个变量是从 dofile() 函数中加载的，由于事先不知道类型，我无法知道这个变量的结构里面有哪些东西，在智能补全的 IDE 里面，将导致无法补全内容，也会将错误放到运行时才暴露。</p>
<h3>Lua 几乎没有官方的功能包</h3>
<p>由于定为为可嵌入的脚本语言，核心很小，基础的东西缺少太多了，比如</p>
<ul>
<li>网络库：官方没有网络库，最出名的 luasocket，其实效率不行的</li>
<li>图形库：官方没有任何平台的图形库</li>
<li>数据库：官方没有数据库的连接方案，网络库都没有，这个可以理解了</li>
<li>数学库：没有高精度的数学库</li>
</ul>
<p>仅有的语言核心，只有基本的输入输出，double 精度的数学操作而已。</p>
<h3>Lua 没有官方的包管理器</h3>
<p>Lua 没有官方的包管理器，这就导致了社区中好的库，应用实践，无法通过权威的渠道沉淀，并传递开来。随处可见普通的 Lua 用户将长时间将 Lua 作为普通的脚本语言，无法应用上顺手的包，解决眼前的问题。而对于高级一点的用户，也没有权威的渠道，来散播其创意和经验。</p>
<p>现有的官方的 <a href="http://lua-users.org/wiki/">Lua Users Wiki</a>，真的弱爆了。推一下 <a href="https://luarocks.org/">LuaRocks</a> 吧，还不错的。</p>
<h3>Lua 没有官方的 IDE</h3>
<p>IDE 是大大降低使用门槛的工具，也是大大提高开发效率的工具，结构化开发，断点调试，这些其他语言 IDE 中都有的东西，在 Lua 官方网站上，你是看不到的。</p>
<p>我自己倒是使用了 vs-code 配合 sumneko 的 <a href="https://github.com/sumneko/lua-language-server">lua-language-server</a> 插件，之前也看了一下 <a href="https://studio.zerobrane.com/">ZeroBrane</a> IDE，但没有用过。</p>
<h3>Lua 不支持多线程</h3>
<p>Lua 核心很小，C89 语法写成，没有规定运行平台，也就没有多线程支持这一说法了。</p>
<p>--</p>
<p>话说，喝了不少酒，吐了一下，舒服多了，:)</p>
<div class="category"><a href="CategoryProgramming.html">CategoryProgramming</a> / <a href="2020-06.html#p0">Permalink</a> / <a href="https://github.com/lalawue/homepage/discussions/categories/blog" target="_blank">Discussion</a></div>
<!-- Page published by cmark-gfm ends here -->
  <div id="foot">2004-<script>var d = new
	Date();document.write(d.getFullYear())</script> &copy;
	Sucha. Powered by MarkdownProjectCompositor.
  </div>
  </div><!-- text -->
  <div id="sidebar">
  </div><!-- sidebar -->
  <script src="../js/prism.min.js" async="async"></script>
  <script src="../js/blog_sidebar.js"></script>
  </div> <!-- body -->
</body>
</html>